package com.cn.stepLock.chain;

import com.cn.stepLock.enums.ChainType;
import com.cn.stepLock.enums.ExecuteStatus;
import com.cn.stepLock.event.ExceptionTaskEvent;
import com.cn.stepLock.event.FinishTaskEvent;
import com.cn.stepLock.event.OfferTaskToParkChainEvent;
import com.cn.stepLock.event.OfferTaskToSynchronizedChainEvent;
import com.cn.stepLock.exception.MessageException;
import com.cn.stepLock.exception.ThrowBackException;
import com.cn.stepLock.lambda.LambdaUtil;
import com.google.common.collect.ImmutableList;
import sun.awt.EventListenerAggregate;

import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

import static com.cn.stepLock.chain.ChainUtils.*;
import static com.cn.stepLock.lambda.LambdaUtil.transferDistinctList;

/**
 * @author Aaron
 * @desc
 * @createTime 2022-02-22 14:06
 * @since
 */
public class ProcessChain extends AbstartChain implements Runnable{

    LinkedBlockBothWayTaskQueue taskLinkedBlockingQueue;

    ProcessChain preChain;

    ProcessChain nextChain;

    int chainType;

    AtomicInteger exceptCount;

    Thread executeThread;

    private int index;

    public ProcessChain() {
        taskLinkedBlockingQueue =  new LinkedBlockBothWayTaskQueue();
        this.chainType = ChainType.UNKNOWN.getValue();
    }

    @Override
    void start(){
        this.executeThread = startThread(this);
    }

    @Override
    void offer(Task task) {
        taskLinkedBlockingQueue.offer(task);
    }

    @Override
    void stop() {
        this.executeThread.interrupt();
    }

    void postInvokeTask(Task task){
        if (isLastChain() || task.finaleFinished || task.throwable!=null){
            if (task.throwable==null) {
                ChainEventApplication.pulishEvent(new FinishTaskEvent(task));
            }else {
                ChainEventApplication.pulishEvent(new ExceptionTaskEvent(task));
            }
            return;
        }
        bindInvokeTask(task);
        task.status=ExecuteStatus.FINISH.getValue();
        if (this.nextChain.chainType==ChainType.SYNCHRONIZED.getValue()) {
            ChainEventApplication.pulishEvent(new OfferTaskToSynchronizedChainEvent(task.nextChainTask,(SynchronizedProcessChain) this.nextChain));
            return;
        }
        ChainEventApplication.pulishEvent(new OfferTaskToParkChainEvent(task.nextChainTask,(ParkProcessChain) this.nextChain));
    }

    void processTask(Task task){
        try {
            task.executeTask();
            if (isLastChain()){
                task.unparkPresentTask = true;
            }
        } catch (Throwable e) {
            task.throwable = e;
            task.unparkPresentTask = true;
        }finally {
            unParkThread(task);
        }
    }

    protected void unParkThread(Task task) {
        LockSupport.unpark(task.ownerThread);
    }


    @Override
    public void executeTask(Task task){
        offer(task);
        /**
         * 挂起客户线程
         */
        LockSupport.park();
        handleUnPark(task);
    }

    @Override
    public void executeTaskList(List<Task> taskList) {
//        offer(taskList);
//        LockSupport.park();
//        handleUnPark(task);
    }

    void handleUnPark(Task task){
        postInvokeTask(task);
    }

    void bindInvokeTask(Task task) {
        Task nextChainTask = task.nextChainTask;
        task.nextStepInvokeTask = nextChainTask;
        nextChainTask.preStepInvoeTaskList.add(task);
    }

    boolean isLastChain(){
        return this.nextChain==null;
    }


    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    @Override
    public void run() {

    }
}
